#import "Alexandra.h"
#import "Article.h"
#import "ArticleSet.h"
#import "NewsgroupSet.h"
#import "Newsgroup.h"
#import "ArticleViewControl.h"
#import "Composer.h"
#import "ColumnMatrix.h"
#import "ArticleSetMatrix.h"
#include <assert.h>
#import <misckit/MiscMailApp.h>
#import <misckit/MiscString.h>
#import <misckit/MiscAppDefaults.h>
#import "response_codes.h"
#import "plain-subject.h"
#import "MatrixScroller.h"
#import "descriptors.h"

@implementation ArticleSet

- init
{
   currentNewsgroup=nil;
   unselAction=@selector(clear);
   [super init];
   return self;
}

- awakeFromNib
{
   char buf1[255];

   sprintf(buf1,"SortType %s",[nntpServer serverName]);
   currentSortType=[NXApp defaultIntValue:buf1];

   return self;
}

- free
{
   [Composer killComposerForServer:nntpServer];
   return [super free];
}

- loadGroup:(Newsgroup *)aNewsgroup
{
   int i,j;
   Article *anArticle;

   if(aNewsgroup==nil)
      return self;

   currentNewsgroup=aNewsgroup;

   //get article list
   myMList=[aNewsgroup articleList];
   [myMatrix setMatrixCellList:myMList];
   
   //choose articles to show. Here: all unread
   j=[myMList count];
   for(i=0;i<j;i++){
      anArticle=[myMList objectAt:i];
      if([anArticle isRead])
         [anArticle unsetTag];
      else
         [anArticle setTag];
   }
   [myMatrix setAutodisplay:NO]; 
   [[myMatrix loadMatrix] scrollUp];
   [[myMatrix setAutodisplay:YES] display];
   [self sync];

   return self;
}

- selectArticle:sender
{
   int statusCode;
   Article *oldSelection;

   [[myMatrix window] makeFirstResponder:myMatrix];

   assert(myMList!=NULL); 
   oldSelection=currentSelection;
   [self syncAndReturnList:NO];

   if(oldSelection==currentSelection)
      return self;
 
   if(numSelCells==1){
      statusCode=[theArticleViewControl loadArticle:currentSelection fromGroup:[[theNewsgroupSet currentSelection] stringValue]];
      if(statusCode!=OK_BODY){
         if(statusCode==-1)
            return self;
         //[myMList removeObject:currentSelection];
         if([currentSelection isRead]==FALSE){
            [[theNewsgroupSet currentSelection] incNumberUnreadArticles:-1];
            [theNewsgroupSet redisplayMatrix];
         }
         [myMatrix perform:@selector(removeInvalidCell:) with:currentSelection afterDelay:0.0 cancelPrevious:YES];
         return self;
      }
      if([currentSelection isRead]==FALSE){
         [currentSelection setRead];
         [[theNewsgroupSet currentSelection] incNumberUnreadArticles:-1];
         [theNewsgroupSet redisplayMatrix];
      }
   } 

   [myMatrix display];
  
   return self;
}

- showAll:sender
{
   if((currentNewsgroup==nil) || (myMList==nil))
      return self;
   [myMList makeObjectsPerform:@selector(setTag)];
   [[myMatrix reloadMatrix] display];

   [myMatrix setButtonTitle:"All Articles"];

   return self;
}

- showUnread:sender
{
   int i,j;
   int oldNumSelCells;

   if((currentNewsgroup==NULL) || (myMList==NULL))
      return self;
   j=[myMList count];
   for(i=0;i<j;i++){
      Article* anArticle;
      anArticle=[myMList objectAt:i];
      if([anArticle isRead]==TRUE)
         [anArticle unsetTag];
      else
         [anArticle setTag];
   }

   if(currentSelection!=nil)
      [currentSelection setTag];

   [[myMatrix reloadMatrix] display];

   oldNumSelCells=numSelCells;
   [self sync];
   
   if((oldNumSelCells!=1)&&(numSelCells==1)){
      currentSelection=nil;
      numSelCells=0;
      [self selectArticle:self];
   }
   [myMatrix setButtonTitle:"Unread Articles"];

   return self;
}

- markSelectionReadOrUnread:(BOOL)flag
{
   /*flag==TRUE <=> Mark unread */
   Article* anArticle;
   int delta;
   List *articleList=nil;
   int i,j;

   if(currentNewsgroup==NULL)
      return self;
   articleList=[myMatrix getCurrSelections];
   j=[articleList count];
   delta=0;
   for(i=0;i<j;i++){
      anArticle=[articleList objectAt:i];
      if([anArticle isRead]==flag){
         if(flag==FALSE){
            [anArticle setRead];
            delta-=1;
         }
         else{
            [anArticle setUnread];
            delta+=1;
         }
      } 
   }
   if(delta!=0){
      [[theNewsgroupSet currentSelection] incNumberUnreadArticles:delta];
      [theNewsgroupSet redisplayMatrix];
      [myMatrix display];
   }
   [articleList free];
   return self;
}

- markRead:sender
{
   [self markSelectionReadOrUnread:FALSE];
   return self;
}

- markUnread:sender
{
   [self markSelectionReadOrUnread:TRUE];
   return self;
}

- followupToSelection:sender
{
   if(currentSelection!=nil){
      if([self postToSelectedNG:self]!=nil)
         [theComposer followup:self];
   }
   return self;
}

- postToSelectedNG:sender
{
   List *selList;
   int i,j;

   EM_DURING
 	
      selList=[[theNewsgroupSet theMatrix] getCurrSelections];

      // warum muss den eine Gruppe ausgewÙhlt sein?
	  EM_CONDERROR(([selList count]==0),ECOMPNeedGroup,NULL,NULL);

      // See if posting is possible in general
 	  if([nntpServer canPost]==FALSE)
		 EM_ERROR(ECOMPNoPosting,NULL,NULL);

      // See if you can post to all groups in list
      j=[selList count];
      for(i=0;i<j;i++){
         char p=[[selList objectAt:i] postable];
    
		 if(p=='n')	
		    EM_ERROR(ECOMPNoPostingToGroup,[[selList objectAt:i] stringValue],NULL);
      }
      // O.K.
	  theComposer=[Composer newWindowForServer:nntpServer];
      [theComposer preparePostTo:selList 
	  	selArticle:currentSelection inView:theArticleViewControl];

   EM_EMPTYHANDLER

   [selList free];
   return self;
}

 
- clearMatrix
{
   currentNewsgroup=nil;
   myMList=nil;
   [myMatrix setMatrixCellList:nil];
   [[myMatrix loadMatrix] display];
   [[myMatrix window] flushWindow];
   [self sync];
   return self;
}

- (const char *)stringValue
{
   return ptr_to_articlebody;
}

- forwardArticle:sender
{
   NXStream *aStream;
   int len,maxlen;
   id subjectStr=[[MiscString init] alloc];
	const char *oldSubjectPtr;
	BOOL is_reply;
	id mailer;
	
   if((currentSelection==nil)||(currentNewsgroup==nil)){
      NXRunAlertPanel("ALEXANDRA","No article selected.",NULL,NULL,NULL);
      return self;
   }

   mailer=[MiscMailApp localMailer];
   if(mailer==nil){
      NXRunAlertPanel("ALEXANDRA","Can't reach mail application.",NULL,NULL,NULL);
      return self;
   }
   
   //O.K. first get the articles body
   aStream=NXOpenMemory(NULL,0,NX_WRITEONLY);
   NXPrintf(aStream,"Forwarded Article %s\n",[currentSelection header]->fieldBody[MSG_ID]);
   NXPrintf(aStream,"Newsgroup %s\n",[currentNewsgroup stringValue]);
   NXPrintf(aStream,"From %s\n\n",[currentSelection header]->fieldBody[FROM]);

   [theArticleViewControl writeBody:aStream];
   NXGetMemoryBuffer(aStream,&ptr_to_articlebody,&len,&maxlen);

   //Compose subject
	oldSubjectPtr=plain_subject((char *)[currentSelection stringValue],&is_reply);
	if(is_reply)
	   [subjectStr setStringValue:"Re: "];
	[subjectStr strcat:oldSubjectPtr];
	
   // Open Compose window in mailer
   [mailer sendMailTo:nil subject:currentSelection body:self];
      
   NXCloseMemory(aStream,NX_FREEBUFFER);
   [subjectStr free];
   return self;
}

- replyByMail:sender
{
   NXStream *aStream;
   int len,maxlen;
   MiscString *to,*subject;
   char *r;
   BOOL dummy;
	id mailer;
	
   if((currentSelection==nil)||(currentNewsgroup==nil)){
      NXRunAlertPanel("ALEXANDRA","No article selected.",NULL,NULL,NULL);
      return self;
   }

   mailer=[MiscMailApp localMailer];
   if(mailer==nil){
      NXRunAlertPanel("ALEXANDRA","Can't reach mail application.",NULL,NULL,NULL);
      return self;
   }
   
   //O.K. first get the articles body
   aStream=NXOpenMemory(NULL,0,NX_WRITEONLY);
   NXPrintf(aStream,"In article %s, you wrote:\n",[currentSelection header]->fieldBody[MSG_ID]);
   [theArticleViewControl writeQuotedText:aStream];
   NXGetMemoryBuffer(aStream,&ptr_to_articlebody,&len,&maxlen);

   //Make Subject string
   subject=[[MiscString alloc] init];
   [subject setStringValue:"Re: "];
	[subject strcat:plain_subject([currentSelection header]->fieldBody[SUBJECT], &dummy)]; 

   //Make To
   to=[[MiscString alloc] init];
   r=[currentSelection header]->fieldBody[REPLY_TO];
   if((r==NULL)||(r[0]=='\0'))
      r=[currentSelection header]->fieldBody[FROM];
   [to setStringValue:r];

   // Open Compose window in mailer
   [mailer sendMailTo:to subject:subject body:self];

   NXCloseMemory(aStream,NX_FREEBUFFER);
   [to free];
	[subject free];
	
   return self;
}

- cancelArticle:sender
{
    char	*messageID;
    char	*mailAddress;
    NXStream	*articlestream;

    if(currentSelection==nil)
       return nil;

    if ((messageID=[currentSelection header]->fieldBody[MSG_ID]) == NULL)
	return nil;
    
    mailAddress = [[FromHeaderController new] validFromAddress];
	if(!mailAddress)
		return self;

    	
    //Post 
    articlestream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
    NXPrintf(articlestream,"Newsgroups: %s\nSubject: cancel\nControl: cancel %s\nFrom: %s\n\ncancel\n",[currentSelection header]->fieldBody[NEWSGROUPS], messageID, mailAddress);
    NXSeek(articlestream, (long)0,NX_FROMSTART);
    [nntpServer postArticle:articlestream];
    
    NXCloseMemory(articlestream,NX_FREEBUFFER);
    free(mailAddress);
    return(self);
}


- setArticleSortType:(int)type
{
   char buf[255];
   int oldType;
   id selNewsgroup;

   sprintf(buf,"SortType %s",[nntpServer serverName]);
   oldType=[NXApp defaultIntValue:buf];
   if(oldType!=type){
      currentSortType=type;
      [NXApp setDefault:buf toInt:type];
      if((selNewsgroup=[theNewsgroupSet currentSelection])!=nil)
         if([selNewsgroup resortIfNeeded:type]){
            [myMatrix reloadMatrix];
            [myMatrix display];
         }
   }

   return self;
}

- sortArticlesByNumber:sender
{
   [self setArticleSortType:SORT_BY_NUMBER];
   return self;
}

- sortArticlesByDate:sender
{
   [self setArticleSortType:SORT_BY_DATE];
   return self;
}

- sortArticlesBySubject:sender
{
   [self setArticleSortType:SORT_BY_SUBJECT];
   return self;
}

- sortArticlesByName:sender
{
   [self setArticleSortType:SORT_BY_REAL_NAME];
   return self;
}

- catchUpThreadAt:(int)index
{
   int i,j;
   id anArticle;
   const char *thread_subject;
   int m=0;
   BOOL dummy;

   thread_subject=plain_subject([[myMatrix cellAt:index :0] header]->fieldBody[SUBJECT],&dummy);
   for(i=0,j=[myMList count];i<j;i++){
      anArticle=[myMList objectAt:i];
      if(!strcmp(plain_subject([anArticle header]->fieldBody[SUBJECT],&dummy),thread_subject)){
         if(![anArticle isRead]){
            [anArticle setRead];
            m++;
         }
      }
   }
   if(m>0)
      [[theNewsgroupSet currentSelection] incNumberUnreadArticles:-1*m];

   return self;
}

- skipThreadAndUpOrDown:(int)delta
{
   int i=[myMatrix selectedRow];
   int a,b,s;

   if(i<0){
      NXRunAlertPanel("ALEXANDRA","No article selected.",NULL,NULL,NULL);
      return self;
   }
   [self catchUpThreadAt:i];
   if(delta!=0){
      [myMatrix getNumRows:&a numCols:&b];

      s=i+delta;
      while((s>=0) && (s<a)){
         id anArticle=[myMatrix cellAt:s :0];
         if(![anArticle isRead]){
            [myMatrix selectCellAt:s :0];
            [self selectArticle:self];
            [myMatrix scrollCellToVisible:s upperOffset:((delta==-1)? 1.5:0.0) lowerOffset:((delta==1)? 1.5:0.0)];
            break;
         }
         s+=delta;
      }
   }
   [myMatrix display];
   [theNewsgroupSet redisplayMatrix];

   return self;
}

- skipThreadAndUp:sender
{
   return [self skipThreadAndUpOrDown:-1];
}

- skipThreadAndDown:sender
{
   return [self skipThreadAndUpOrDown:1];
}

- (const char *)stringValueForCellAt:(int)index
{
   int h=[NXApp defaultIntValue:"findGeneralScope"];
   if((h>=0)&&(h<XOVER_COUNT)){
      char *s=[[myMList objectAt:index] header]->fieldBody[h];
      if(s!=NULL)
         return s;
   }
   return "";
}

- (BOOL)selectArticleWithMsgid:(const char *)msgid
{
	int i,j;
	
	for(i=0,j=[myMList count];i<j;i++){
		const char *aMsgid=[[myMList objectAt:i] header]->fieldBody[MSG_ID];
		if(aMsgid && !strcmp(aMsgid,msgid))
			return [self selectArticleWithNumber:[[myMList objectAt:i] number]];
	}
	
	return FALSE;
}

- (BOOL)selectArticleWithNumber:(long)snumber;
{
	int i,j;
	
	for(i=0,j=[myMList count];i<j;i++){
		id aCell=[myMList objectAt:i];
		if([aCell number]==snumber){
			if(![aCell isTaged])
				[self showAll:self];
			return [myMatrix selectArticleWithNumber:snumber];
		}
	}
	
	return FALSE;
}

@end
